{
 "metadata": {
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.12-final"
  },
  "orig_nbformat": 2,
  "kernelspec": {
   "name": "python3",
   "display_name": "Python 3.6.12 64-bit ('Begin': conda)",
   "metadata": {
    "interpreter": {
     "hash": "5418aa4018a4a6d4e7a95d1864632b051ddd71a0141746edebf6fac21ca2c6f9"
    }
   }
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2,
 "cells": [
  {
   "source": [
    "# C08. 异常"
   ],
   "cell_type": "markdown",
   "metadata": {}
  },
  {
   "source": [
    "## 8.1 异常概述\n",
    "\n",
    "Python 使用异常对象来表示异常状态，并且在遇到错误时引发异常。异常对象如果没有被处理(或者捕获)时，程序将终止并且显示一条错误信息(Traceback)。\n"
   ],
   "cell_type": "markdown",
   "metadata": {}
  },
  {
   "source": [
    "## 8.2 异常控制"
   ],
   "cell_type": "markdown",
   "metadata": {}
  },
  {
   "source": [
    "### 8.2.1 raise\n",
    "\n",
    "raise 语句可以引发异常，并且将一个类(必须是 Exception 的子类) 或者实例作为参数。\n",
    "\n",
    "表 8-1 一些内置的异常类\n",
    "-   Exception：几乎所有的异常类都是从它派生出来\n",
    "-   AttributeError：引用属性或者给属性赋值失败时引发\n",
    "-   OSError：操作系统不能执行指定的任务时引发，有多个子类。例如：打开文件\n",
    "-   IndexError：使用序列中不存在的索引时引发，为 LookupError 的子类\n",
    "-   KeyError：使用映射中不存在的键时引发，为 LookupError 的子类\n",
    "-   NameError：找不到名称(变量)时引发\n",
    "-   SyntaxError：代码不正确时引发\n",
    "-   TypeError：将内置操作或者函数用于类型不正确的对象时引发\n",
    "-   ValueError：将内置操作或者函数用于值不正确的对象时引发，例如：类型正确但是包含的值不合适\n",
    "-   ZeroDivisionError：在除法或者求模运算的第二参数为零时引发"
   ],
   "cell_type": "markdown",
   "metadata": {}
  },
  {
   "source": [
    "### 8.2.2 自定义异常类"
   ],
   "cell_type": "markdown",
   "metadata": {}
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 必须直接或者间接地继承自 Exception\n",
    "class CustomException(Exception):\n",
    "    pass"
   ]
  },
  {
   "source": [
    "## 8.3 捕获异常\n",
    "\n",
    "注意：异常从函数向外传播到调用函数的地方。如果调用函数的代码处没有捕获异常，程序就会将异常向最顶层传播。"
   ],
   "cell_type": "markdown",
   "metadata": {}
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "output_type": "stream",
     "name": "stdout",
     "text": [
      "不能除0\n"
     ]
    }
   ],
   "source": [
    "# 异常捕获代码块\n",
    "try:\n",
    "    1/0\n",
    "except ZeroDivisionError:\n",
    "    print(\"不能除0\")"
   ]
  },
  {
   "source": [
    "### 8.3.1 继续抛出异常"
   ],
   "cell_type": "markdown",
   "metadata": {}
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "output_type": "stream",
     "name": "stdout",
     "text": [
      "不能除0\n又被我抓到了\n"
     ]
    }
   ],
   "source": [
    "try:\n",
    "    try:\n",
    "        1/0\n",
    "    except ZeroDivisionError:\n",
    "        print(\"不能除0\")\n",
    "        raise   # 再次抛出相同的异常\n",
    "except ZeroDivisionError:\n",
    "    print(\"又被我抓到了\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "output_type": "stream",
     "name": "stdout",
     "text": [
      "不能除0\n值有错\n"
     ]
    }
   ],
   "source": [
    "# 抛出指定的异常\n",
    "try:\n",
    "    try:\n",
    "        1/0\n",
    "    except ZeroDivisionError:\n",
    "        print(\"不能除0\")\n",
    "        raise ValueError\n",
    "except ValueError:\n",
    "    print(\"值有错\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 33,
   "metadata": {},
   "outputs": [
    {
     "output_type": "stream",
     "name": "stdout",
     "text": [
      "值有错误\n"
     ]
    },
    {
     "output_type": "error",
     "ename": "Exception",
     "evalue": "",
     "traceback": [
      "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[1;31mZeroDivisionError\u001b[0m                         Traceback (most recent call last)",
      "\u001b[1;32m<ipython-input-33-1aba6867d337>\u001b[0m in \u001b[0;36m<module>\u001b[1;34m\u001b[0m\n\u001b[0;32m      3\u001b[0m     \u001b[1;32mtry\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m----> 4\u001b[1;33m         \u001b[1;36m1\u001b[0m\u001b[1;33m/\u001b[0m\u001b[1;36m0\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m      5\u001b[0m     \u001b[1;32mexcept\u001b[0m \u001b[0mZeroDivisionError\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n",
      "\u001b[1;31mZeroDivisionError\u001b[0m: division by zero",
      "\nDuring handling of the above exception, another exception occurred:\n",
      "\u001b[1;31mValueError\u001b[0m                                Traceback (most recent call last)",
      "\u001b[1;32m<ipython-input-33-1aba6867d337>\u001b[0m in \u001b[0;36m<module>\u001b[1;34m\u001b[0m\n\u001b[0;32m      6\u001b[0m         \u001b[1;31m# from None 就不会输出引起异常的原因\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m----> 7\u001b[1;33m         \u001b[1;32mraise\u001b[0m \u001b[0mValueError\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;34m\"值有错误\"\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m      8\u001b[0m \u001b[1;32mexcept\u001b[0m \u001b[0mException\u001b[0m \u001b[1;32mas\u001b[0m \u001b[0me\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n",
      "\u001b[1;31mValueError\u001b[0m: 值有错误",
      "\nDuring handling of the above exception, another exception occurred:\n",
      "\u001b[1;31mException\u001b[0m                                 Traceback (most recent call last)",
      "\u001b[1;32m<ipython-input-33-1aba6867d337>\u001b[0m in \u001b[0;36m<module>\u001b[1;34m\u001b[0m\n\u001b[0;32m      8\u001b[0m \u001b[1;32mexcept\u001b[0m \u001b[0mException\u001b[0m \u001b[1;32mas\u001b[0m \u001b[0me\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m      9\u001b[0m     \u001b[0mprint\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0me\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m---> 10\u001b[1;33m     \u001b[1;32mraise\u001b[0m \u001b[0mException\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m",
      "\u001b[1;31mException\u001b[0m: "
     ]
    }
   ],
   "source": [
    "# 异常的上下文\n",
    "try:\n",
    "    try:\n",
    "        1/0\n",
    "    except ZeroDivisionError:\n",
    "        # from None 就不会输出引起异常的原因\n",
    "        raise ValueError(\"值有错误\") \n",
    "except Exception as e:\n",
    "    print(e)\n",
    "    raise Exception"
   ]
  },
  {
   "source": [
    "### 8.3.2 捕获多个异常：多个 except 子句"
   ],
   "cell_type": "markdown",
   "metadata": {}
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "metadata": {},
   "outputs": [
    {
     "output_type": "stream",
     "name": "stdout",
     "text": [
      "输入的不是数字\n"
     ]
    }
   ],
   "source": [
    "try:\n",
    "    x=int(input(\"输入第一个数字：\"))\n",
    "    y=int(input(\"输入第二个数字：\"))\n",
    "    print(x/y)\n",
    "except ZeroDivisionError:\n",
    "    print(\"第二个数字是0\")\n",
    "except ValueError:\n",
    "    print(\"输入的不是数字\")"
   ]
  },
  {
   "source": [
    "### 8.3.3 捕获多个异常：元组"
   ],
   "cell_type": "markdown",
   "metadata": {}
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "metadata": {},
   "outputs": [
    {
     "output_type": "stream",
     "name": "stdout",
     "text": [
      "输入的数字有错误\n"
     ]
    }
   ],
   "source": [
    "try:\n",
    "    x=int(input(\"输入第一个数字：\"))\n",
    "    y=int(input(\"输入第二个数字：\"))\n",
    "    print(x/y)\n",
    "except (ZeroDivisionError,ValueError):\n",
    "    print(\"输入的数字有错误\")"
   ]
  },
  {
   "source": [
    "### 8.3.4 捕获异常的对象"
   ],
   "cell_type": "markdown",
   "metadata": {}
  },
  {
   "cell_type": "code",
   "execution_count": 25,
   "metadata": {},
   "outputs": [
    {
     "output_type": "stream",
     "name": "stdout",
     "text": [
      "invalid literal for int() with base 10: 'a'\n"
     ]
    }
   ],
   "source": [
    "try:\n",
    "    x=int(input(\"输入第一个数字：\"))\n",
    "    y=int(input(\"输入第二个数字：\"))\n",
    "    print(x/y)\n",
    "except (ZeroDivisionError,ValueError) as e:\n",
    "    print(e)"
   ]
  },
  {
   "source": [
    "### 8.3.5 捕获所有的异常：except\n",
    "\n",
    "注意：这样处理异常会导致某些未知的异常没有得到妥善处理，影响程序未来的稳定性。"
   ],
   "cell_type": "markdown",
   "metadata": {}
  },
  {
   "cell_type": "code",
   "execution_count": 28,
   "metadata": {},
   "outputs": [
    {
     "output_type": "stream",
     "name": "stdout",
     "text": [
      "输入的数字有错误\n"
     ]
    }
   ],
   "source": [
    "try:\n",
    "    x=int(input(\"输入第一个数字：\"))\n",
    "    y=int(input(\"输入第二个数字：\"))\n",
    "    print(x/y)\n",
    "except:\n",
    "    print(\"输入的数字有错误\")"
   ]
  },
  {
   "source": [
    "### 8.3.6 捕获无异常状态：else"
   ],
   "cell_type": "markdown",
   "metadata": {}
  },
  {
   "cell_type": "code",
   "execution_count": 29,
   "metadata": {},
   "outputs": [
    {
     "output_type": "stream",
     "name": "stdout",
     "text": [
      "0.5\n一切顺利！\n"
     ]
    }
   ],
   "source": [
    "try:\n",
    "    x=int(input(\"输入第一个数字：\"))\n",
    "    y=int(input(\"输入第二个数字：\"))\n",
    "    print(x/y)\n",
    "except:\n",
    "    print(\"输入的数字有错误\")\n",
    "else:\n",
    "    print(\"一切顺利！\")"
   ]
  },
  {
   "source": [
    "### 8.3.7 异常处理模块的清理工作：finally"
   ],
   "cell_type": "markdown",
   "metadata": {}
  },
  {
   "cell_type": "code",
   "execution_count": 30,
   "metadata": {},
   "outputs": [
    {
     "output_type": "stream",
     "name": "stdout",
     "text": [
      "0.5\n一切顺利！\n清理工作完成！\n"
     ]
    }
   ],
   "source": [
    "try:\n",
    "    x=int(input(\"输入第一个数字：\"))\n",
    "    y=int(input(\"输入第二个数字：\"))\n",
    "    print(x/y)\n",
    "except:\n",
    "    print(\"输入的数字有错误\")\n",
    "else:\n",
    "    print(\"一切顺利！\")\n",
    "finally:\n",
    "    print(\"清理工作完成！\")"
   ]
  },
  {
   "source": [
    "## 8.4 异常和函数\n",
    "\n",
    "如果异常没有在引发它的函数中处理，则将向上传播到调用函数的地方；如果异常没有在调用地方处理，则会继续向上传播，直到到达主程序(全局作用域)。"
   ],
   "cell_type": "markdown",
   "metadata": {}
  },
  {
   "cell_type": "code",
   "execution_count": 36,
   "metadata": {},
   "outputs": [
    {
     "output_type": "stream",
     "name": "stdout",
     "text": [
      "这里处理所有问题\n"
     ]
    }
   ],
   "source": [
    "def faulty():\n",
    "    raise Exception(\"Sth. is wrong\")\n",
    "\n",
    "def ignore_exception():\n",
    "    faulty()\n",
    "\n",
    "def handle_exception():\n",
    "    try:\n",
    "        faulty()\n",
    "    except:\n",
    "        print(\"这里处理所有问题\")    \n",
    "\n",
    "handle_exception()        "
   ]
  },
  {
   "source": [
    "## 8.5 异常之禅"
   ],
   "cell_type": "markdown",
   "metadata": {}
  },
  {
   "cell_type": "code",
   "execution_count": 39,
   "metadata": {},
   "outputs": [
    {
     "output_type": "stream",
     "name": "stdout",
     "text": [
      "Description of zYx\nAge: 47\n------------------------------\nDescription of zYx\nAge: 47\nOccupation: coder\n"
     ]
    }
   ],
   "source": [
    "def describe_person(person):\n",
    "    print(\"Description of\", person['name'])\n",
    "    print(\"Age:\", person['age'])\n",
    "    # if 'occupation' in person:\n",
    "    try:\n",
    "        print(\"Occupation:\", person['occupation'])\n",
    "    except KeyError:\n",
    "        pass\n",
    "person={'name':'zYx', 'age':47}\n",
    "describe_person(person)\n",
    "print('-'*30)\n",
    "person['occupation']='coder'\n",
    "describe_person(person)"
   ]
  },
  {
   "source": [
    "## 8.6 警告：不重要的异常"
   ],
   "cell_type": "markdown",
   "metadata": {}
  },
  {
   "cell_type": "code",
   "execution_count": 49,
   "metadata": {},
   "outputs": [
    {
     "output_type": "error",
     "ename": "UserWarning",
     "evalue": "I've got a bad feeling about this.",
     "traceback": [
      "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[1;31mUserWarning\u001b[0m                               Traceback (most recent call last)",
      "\u001b[1;32m<ipython-input-49-cfc80bb798b4>\u001b[0m in \u001b[0;36m<module>\u001b[1;34m\u001b[0m\n\u001b[0;32m      2\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m      3\u001b[0m \u001b[0mfilterwarnings\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;34m'error'\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m----> 4\u001b[1;33m \u001b[0mwarn\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;34m\"I've got a bad feeling about this.\"\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m",
      "\u001b[1;31mUserWarning\u001b[0m: I've got a bad feeling about this."
     ]
    }
   ],
   "source": [
    "from warnings import warn, filterwarnings\n",
    "\n",
    "filterwarnings('error')\n",
    "warn(\"I've got a bad feeling about this.\")"
   ]
  },
  {
   "source": [
    "## 8.7小结\n",
    "\n",
    "-   异常对象：用于表示异常情况。异常需要处理，否则会导致程序终止。\n",
    "-   引发异常：使用 raise 语句引发异常。将一个异常类或者异常实例作为参数，也可提供两个参数(异常和错误消息)。\n",
    "-   自定义的异常类：通过从 Exception 派出来创建自定义的异常。\n",
    "-   捕获异常：except 捕获异常。\n",
    "    -   捕获指定异常：except+异常类名，except+(异常类元组)\n",
    "    -   捕获所有异常：except 后什么都没有\n",
    "-   无异常情况：else 处理无异常情况\n",
    "-   清理异常现场：finally 清理捕捉异常模块后的现场\n",
    "-   异常和函数：在函数中引发异常时，异常将传播到调用函数的地方\n",
    "-   警告：警告类似于异常，但是(通常)只打印一条错误消息。可以指定警告的类别，警告类别是 Warning 的子类。"
   ],
   "cell_type": "markdown",
   "metadata": {}
  }
 ]
}